{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "---\n",
    "sidebar_position: 1\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Build a Chatbot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Overview\n",
    "\n",
    "We'll go over an example of how to design and implement an LLM-powered chatbot. \n",
    "This chatbot will be able to have a conversation and remember previous interactions.\n",
    "\n",
    "\n",
    "Note that this chatbot that we build will only use the language model to have a conversation.\n",
    "There are several other related concepts that you may be looking for:\n",
    "\n",
    "- [Conversational RAG](/docs/tutorials/qa_chat_history): Enable a chatbot experience over an external source of data\n",
    "- [Agents](/docs/tutorials/agents): Build a chatbot that can take actions\n",
    "\n",
    "This tutorial will cover the basics which will be helpful for those two more advanced topics, but feel free to skip directly to there should you choose.\n",
    "\n",
    "\n",
    "## Concepts\n",
    "\n",
    "Here are a few of the high-level components we'll be working with:\n",
    "\n",
    "- [`Chat Models`](/docs/concepts/#chat-models). The chatbot interface is based around messages rather than raw text, and therefore is best suited to Chat Models rather than text LLMs.\n",
    "- [`Prompt Templates`](/docs/concepts/#prompt-templates), which simplify the process of assembling prompts that combine default messages, user input, chat history, and (optionally) additional retrieved context.\n",
    "- [`Chat History`](/docs/concepts/#chat-history), which allows a chatbot to \"remember\" past interactions and take them into account when responding to followup questions. \n",
    "- Debugging and tracing your application using [LangSmith](/docs/concepts/#langsmith)\n",
    "\n",
    "We'll cover how to fit the above components together to create a powerful conversational chatbot.\n",
    "\n",
    "## Setup\n",
    "\n",
    "### Installation\n",
    "\n",
    "To install LangChain run:\n",
    "\n",
    "```{=mdx}\n",
    "import Npm2Yarn from \"@theme/Npm2Yarn\"\n",
    "\n",
    "<Npm2Yarn>\n",
    "  langchain\n",
    "</Npm2Yarn>\n",
    "```\n",
    "\n",
    "\n",
    "For more details, see our [Installation guide](/docs/how_to/installation).\n",
    "\n",
    "### LangSmith\n",
    "\n",
    "Many of the applications you build with LangChain will contain multiple steps with multiple invocations of LLM calls.\n",
    "As these applications get more and more complex, it becomes crucial to be able to inspect what exactly is going on inside your chain or agent.\n",
    "The best way to do this is with [LangSmith](https://smith.langchain.com).\n",
    "\n",
    "After you sign up at the link above, make sure to set your environment variables to start logging traces:\n",
    "\n",
    "```shell\n",
    "export LANGCHAIN_TRACING_V2=\"true\"\n",
    "export LANGCHAIN_API_KEY=\"...\"\n",
    "```\n",
    "\n",
    "## Quickstart\n",
    "\n",
    "First up, let's learn how to use a language model by itself. LangChain supports many different language models that you can use interchangably - select the one you want to use below!\n",
    "\n",
    "```{=mdx}\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs openaiParams={`model=\"gpt-3.5-turbo\"`} />\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's first use the model directly. `ChatModel`s are instances of LangChain \"Runnables\", which means they expose a standard interface for interacting with them. To just simply call the model, we can pass in a list of messages to the `.invoke` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m\"Hello Bob, it's nice to meet you! I'm an AI assistant created by Anthropic. How are you doing today?\"\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: {\n",
       "      id: \u001b[32m\"msg_015Qvu91azZviks5VzGvYT7z\"\u001b[39m,\n",
       "      type: \u001b[32m\"message\"\u001b[39m,\n",
       "      role: \u001b[32m\"assistant\"\u001b[39m,\n",
       "      model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "      stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "      usage: { input_tokens: \u001b[33m12\u001b[39m, output_tokens: \u001b[33m30\u001b[39m },\n",
       "      stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "    },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m\"Hello Bob, it's nice to meet you! I'm an AI assistant created by Anthropic. How are you doing today?\"\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: {\n",
       "    id: \u001b[32m\"msg_015Qvu91azZviks5VzGvYT7z\"\u001b[39m,\n",
       "    type: \u001b[32m\"message\"\u001b[39m,\n",
       "    role: \u001b[32m\"assistant\"\u001b[39m,\n",
       "    model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "    stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "    usage: { input_tokens: \u001b[33m12\u001b[39m, output_tokens: \u001b[33m30\u001b[39m },\n",
       "    stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "  },\n",
       "  response_metadata: {\n",
       "    id: \u001b[32m\"msg_015Qvu91azZviks5VzGvYT7z\"\u001b[39m,\n",
       "    model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "    stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "    usage: { input_tokens: \u001b[33m12\u001b[39m, output_tokens: \u001b[33m30\u001b[39m },\n",
       "    stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import { HumanMessage } from \"@langchain/core/messages\";\n",
    "\n",
    "await model.invoke([new HumanMessage({ content: \"Hi! I'm Bob\" })]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The model on its own does not have any concept of state. For example, if you ask a followup question:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m\"I'm afraid I don't actually know your name. I'm Claude, an AI assistant created by Anthropic.\"\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: {\n",
       "      id: \u001b[32m\"msg_01TNDCwsU7ruVoqJwjKqNrzJ\"\u001b[39m,\n",
       "      type: \u001b[32m\"message\"\u001b[39m,\n",
       "      role: \u001b[32m\"assistant\"\u001b[39m,\n",
       "      model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "      stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "      usage: { input_tokens: \u001b[33m12\u001b[39m, output_tokens: \u001b[33m27\u001b[39m },\n",
       "      stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "    },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m\"I'm afraid I don't actually know your name. I'm Claude, an AI assistant created by Anthropic.\"\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: {\n",
       "    id: \u001b[32m\"msg_01TNDCwsU7ruVoqJwjKqNrzJ\"\u001b[39m,\n",
       "    type: \u001b[32m\"message\"\u001b[39m,\n",
       "    role: \u001b[32m\"assistant\"\u001b[39m,\n",
       "    model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "    stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "    usage: { input_tokens: \u001b[33m12\u001b[39m, output_tokens: \u001b[33m27\u001b[39m },\n",
       "    stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "  },\n",
       "  response_metadata: {\n",
       "    id: \u001b[32m\"msg_01TNDCwsU7ruVoqJwjKqNrzJ\"\u001b[39m,\n",
       "    model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "    stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "    usage: { input_tokens: \u001b[33m12\u001b[39m, output_tokens: \u001b[33m27\u001b[39m },\n",
       "    stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await model.invoke([new HumanMessage({ content: \"What's my name?\" })])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's take a look at the example [LangSmith trace](https://smith.langchain.com/public/e5a0ae1b-32b9-4beb-836d-38f40bfa6762/r)\n",
    "\n",
    "We can see that it doesn't take the previous conversation turn into context, and cannot answer the question.\n",
    "This makes for a terrible chatbot experience!\n",
    "\n",
    "To get around this, we need to pass the entire conversation history into the model. Let's see what happens when we do that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m\"You said your name is Bob.\"\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: {\n",
       "      id: \u001b[32m\"msg_01AEQMme3Z1MFKHW8PeDBJ7g\"\u001b[39m,\n",
       "      type: \u001b[32m\"message\"\u001b[39m,\n",
       "      role: \u001b[32m\"assistant\"\u001b[39m,\n",
       "      model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "      stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "      usage: { input_tokens: \u001b[33m33\u001b[39m, output_tokens: \u001b[33m10\u001b[39m },\n",
       "      stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "    },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m\"You said your name is Bob.\"\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: {\n",
       "    id: \u001b[32m\"msg_01AEQMme3Z1MFKHW8PeDBJ7g\"\u001b[39m,\n",
       "    type: \u001b[32m\"message\"\u001b[39m,\n",
       "    role: \u001b[32m\"assistant\"\u001b[39m,\n",
       "    model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "    stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "    usage: { input_tokens: \u001b[33m33\u001b[39m, output_tokens: \u001b[33m10\u001b[39m },\n",
       "    stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "  },\n",
       "  response_metadata: {\n",
       "    id: \u001b[32m\"msg_01AEQMme3Z1MFKHW8PeDBJ7g\"\u001b[39m,\n",
       "    model: \u001b[32m\"claude-3-sonnet-20240229\"\u001b[39m,\n",
       "    stop_sequence: \u001b[1mnull\u001b[22m,\n",
       "    usage: { input_tokens: \u001b[33m33\u001b[39m, output_tokens: \u001b[33m10\u001b[39m },\n",
       "    stop_reason: \u001b[32m\"end_turn\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import { AIMessage } from \"@langchain/core/messages\"\n",
    "\n",
    "await model.invoke(\n",
    "  [\n",
    "    new HumanMessage({ content: \"Hi! I'm Bob\" }),\n",
    "    new AIMessage({ content: \"Hello Bob! How can I assist you today?\" }),\n",
    "    new HumanMessage({ content: \"What's my name?\" }),\n",
    "  ]\n",
    ");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now we can see that we get a good response!\n",
    "\n",
    "This is the basic idea underpinning a chatbot's ability to interact conversationally.\n",
    "So how do we best implement this?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Message History\n",
    "\n",
    "We can use a Message History class to wrap our model and make it stateful.\n",
    "This will keep track of inputs and outputs of the model, and store them in some datastore.\n",
    "Future interactions will then load those messages and pass them into the chain as part of the input.\n",
    "Let's see how to use this!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We import the relevant classes and set up our chain which wraps the model and adds in this message history. A key part here is the function we pass into as the `getSessionHistory()`. This function is expected to take in a `sessionId` and return a Message History object. This `sessionId` is used to distinguish between separate conversations, and should be passed in as part of the config when calling the new chain.\n",
    "\n",
    "Let's also create a simple chain by adding a prompt to help with formatting:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "// We use an ephemeral, in-memory chat history for this demo.\n",
    "import { InMemoryChatMessageHistory } from \"@langchain/core/chat_history\";\n",
    "import { ChatPromptTemplate } from \"@langchain/core/prompts\";\n",
    "import { RunnableWithMessageHistory } from \"@langchain/core/runnables\";\n",
    "\n",
    "const messageHistories: Record<string, InMemoryChatMessageHistory> = {};\n",
    "\n",
    "const prompt = ChatPromptTemplate.fromMessages([\n",
    "  [\"system\", `You are a helpful assistant who remembers all details the user shares with you.`],\n",
    "  [\"placeholder\", \"{chat_history}\"],\n",
    "  [\"human\", \"{input}\"],\n",
    "]);\n",
    "\n",
    "const chain = prompt.pipe(model);\n",
    "\n",
    "const withMessageHistory = new RunnableWithMessageHistory({\n",
    "  runnable: chain,\n",
    "  getMessageHistory: async (sessionId) => {\n",
    "    if (messageHistories[sessionId] === undefined) {\n",
    "      messageHistories[sessionId] = new InMemoryChatMessageHistory();\n",
    "    }\n",
    "    return messageHistories[sessionId];\n",
    "  },\n",
    "  inputMessagesKey: \"input\",\n",
    "  historyMessagesKey: \"chat_history\",\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now need to create a `config` that we pass into the runnable every time. This config contains information that is not part of the input directly, but is still useful. In this case, we want to include a `session_id`. This should look like:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m\"Hi Bob, nice to meet you! I'm an AI assistant. I'll remember that your name is Bob as we continue ou\"\u001b[39m... 110 more characters"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const config = {\n",
    "  configurable: {\n",
    "    sessionId: \"abc2\"\n",
    "  }\n",
    "};\n",
    "\n",
    "const response = await withMessageHistory.invoke({\n",
    "  input: \"Hi! I'm Bob\",\n",
    "}, config);\n",
    "\n",
    "response.content;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m\"Your name is Bob. You introduced yourself as Bob at the start of our conversation.\"\u001b[39m"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const followupResponse = await withMessageHistory.invoke({\n",
    "  input: \"What's my name?\",\n",
    "}, config);\n",
    "\n",
    "followupResponse.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Great! Our chatbot now remembers things about us. If we change the config to reference a different `session_id`, we can see that it starts the conversation fresh."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m\"I'm afraid I don't actually know your name. As an AI assistant without any prior context about you, \"\u001b[39m... 61 more characters"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const config = {\n",
    "  configurable: {\n",
    "    sessionId: \"abc3\"\n",
    "  }\n",
    "};\n",
    "\n",
    "const response = await withMessageHistory.invoke({\n",
    "  input: \"What's my name?\",\n",
    "}, config);\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, we can always go back to the original conversation (since we are persisting it in a database)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m`Your name is Bob. I clearly remember you telling me \"Hi! I'm Bob\" when we started talking.`\u001b[39m"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const config = {\n",
    "  configurable: {\n",
    "    sessionId: \"abc2\"\n",
    "  }\n",
    "};\n",
    "\n",
    "const response = await withMessageHistory.invoke({\n",
    "  input: \"What's my name?\",\n",
    "}, config);\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is how we can support a chatbot having conversations with many users!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Managing Conversation History\n",
    "\n",
    "One important concept to understand when building chatbots is how to manage conversation history. If left unmanaged, the list of messages will grow unbounded and potentially overflow the context window of the LLM. Therefore, it is important to add a step that limits the size of the messages you are passing in.\n",
    "\n",
    "**Importantly, you will want to do this BEFORE the prompt template but AFTER you load previous messages from Message History.**\n",
    "\n",
    "We can do this by adding a simple step in front of the prompt that modifies the `chat_history` key appropriately, and then wrap that new chain in the Message History class. First, let's define a function that will modify the messages passed in. Let's make it so that it selects the 10 most recent messages. We can then create a new chain by adding that at the start."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import type { BaseMessage } from \"@langchain/core/messages\";\n",
    "import { RunnablePassthrough, RunnableSequence } from \"@langchain/core/runnables\";\n",
    "\n",
    "const filterMessages = ({ chat_history }: { chat_history: BaseMessage[]; }) => {\n",
    "  return chat_history.slice(-10);\n",
    "};\n",
    "\n",
    "const chain = RunnableSequence.from([\n",
    "  RunnablePassthrough.assign({\n",
    "    chat_history: filterMessages\n",
    "  }),\n",
    "  prompt,\n",
    "  model,\n",
    "]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now try it out! If we create a list of messages more than 10 messages long, we can see what it no longer remembers information in the early messages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "const messages = [\n",
    "  new HumanMessage({ content: \"hi! I'm bob\" }),\n",
    "  new AIMessage({ content: \"hi!\" }),\n",
    "  new HumanMessage({ content: \"I like vanilla ice cream\" }),\n",
    "  new AIMessage({ content: \"nice\" }),\n",
    "  new HumanMessage({ content: \"whats 2 + 2\" }),\n",
    "  new AIMessage({ content: \"4\" }),\n",
    "  new HumanMessage({ content: \"thanks\" }),\n",
    "  new AIMessage({ content: \"No problem!\" }),\n",
    "  new HumanMessage({ content: \"having fun?\" }),\n",
    "  new AIMessage({ content: \"yes!\" }),\n",
    "  new HumanMessage({ content: \"That's great!\" }),\n",
    "  new AIMessage({ content: \"yes it is!\" }),\n",
    "];"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m\"I'm afraid I don't actually know your name. You haven't provided that detail to me yet.\"\u001b[39m"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const response = await chain.invoke(\n",
    "  {\n",
    "    chat_history: messages,\n",
    "    input: \"what's my name?\"\n",
    "  }\n",
    ")\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But if we ask about information that is within the last ten messages, it still remembers it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m\"You said earlier that you like vanilla ice cream.\"\u001b[39m"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const response = await chain.invoke(\n",
    "  {\n",
    "    chat_history: messages,\n",
    "    input: \"what's my fav ice cream\"\n",
    "  }\n",
    ")\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now wrap this chain in a `RunnableWithMessageHistory` constructor. For demo purposes, we will also slightly modify our `getMessageHistory()` method to always start new sessions with the previously declared list of 10 messages to simulate several conversation turns:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m\"I'm afraid I don't actually know your name since you haven't provided it to me yet.  I don't have pe\"\u001b[39m... 66 more characters"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const messageHistories: Record<string, InMemoryChatMessageHistory> = {};\n",
    "\n",
    "const withMessageHistory = new RunnableWithMessageHistory({\n",
    "  runnable: chain,\n",
    "  getMessageHistory: async (sessionId) => {\n",
    "    if (messageHistories[sessionId] === undefined) {\n",
    "      const messageHistory = new InMemoryChatMessageHistory();\n",
    "      await messageHistory.addMessages(messages);\n",
    "      messageHistories[sessionId] = messageHistory;\n",
    "    }\n",
    "    return messageHistories[sessionId];\n",
    "  },\n",
    "  inputMessagesKey: \"input\",\n",
    "  historyMessagesKey: \"chat_history\",\n",
    "})\n",
    "\n",
    "const config = {\n",
    "  configurable: {\n",
    "    sessionId: \"abc4\"\n",
    "  }\n",
    "};\n",
    "\n",
    "const response = await withMessageHistory.invoke(\n",
    "  {\n",
    "    input: \"whats my name?\",\n",
    "  },\n",
    "  config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There's now two new messages in the chat history. This means that even more information that used to be accessible in our conversation history is no longer available!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32m\"I'm sorry, I don't have any information about your favorite ice cream flavor since you haven't share\"\u001b[39m... 167 more characters"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const response = await withMessageHistory.invoke({\n",
    "  input: \"whats my favorite ice cream?\"\n",
    "}, config);\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you take a look at LangSmith, you can see exactly what is happening under the hood in the [LangSmith trace](https://smith.langchain.com/public/ebc2e1e7-0703-43f7-a476-8cb8cbd7f61a/r). Navigate to the chat model call to see exactly which messages are getting filtered out."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Streaming\n",
    "\n",
    "Now we've got a functional chatbot. However, one *really* important UX consideration for chatbot application is streaming. LLMs can sometimes take a while to respond, and so in order to improve the user experience one thing that most application do is stream back each token as it is generated. This allows the user to see progress.\n",
    "\n",
    "It's actually super easy to do this!\n",
    "\n",
    "All chains expose a `.stream()` method, and ones that use message history are no different. We can simply use that method to get back a streaming response."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "| \n",
      "| Hi\n",
      "|  Tod\n",
      "| d!\n",
      "|  Here\n",
      "| 's\n",
      "|  a\n",
      "|  silly\n",
      "|  joke\n",
      "|  for\n",
      "|  you\n",
      "| :\n",
      "| \n",
      "\n",
      "Why\n",
      "|  di\n",
      "| d the\n",
      "|  tom\n",
      "| ato\n",
      "|  turn\n",
      "|  re\n",
      "| d?\n",
      "|  Because\n",
      "|  it\n",
      "|  saw\n",
      "|  the\n",
      "|  sal\n",
      "| a\n",
      "| d \n",
      "| dressing\n",
      "| !\n",
      "| \n",
      "| \n"
     ]
    }
   ],
   "source": [
    "const config = {\n",
    "  configurable: {\n",
    "    sessionId: \"abc6\"\n",
    "  }\n",
    "};\n",
    "\n",
    "const stream = await withMessageHistory.stream({\n",
    "  input: \"hi! I'm todd. tell me a joke\",\n",
    "}, config);\n",
    "\n",
    "for await (const chunk of stream) {\n",
    "  console.log(\"|\", chunk.content);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Next Steps\n",
    "\n",
    "Now that you understand the basics of how to create a chatbot in LangChain, some more advanced tutorials you may be interested in are:\n",
    "\n",
    "- [Conversational RAG](/docs/tutorials/qa_chat_history): Enable a chatbot experience over an external source of data\n",
    "- [Agents](/docs/tutorials/agents): Build a chatbot that can take actions\n",
    "\n",
    "If you want to dive deeper on specifics, some things worth checking out are:\n",
    "\n",
    "- [Streaming](/docs/how_to/streaming): streaming is *crucial* for chat applications\n",
    "- [How to add message history](/docs/how_to/message_history): for a deeper dive into all things related to message history"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Deno",
   "language": "typescript",
   "name": "deno"
  },
  "language_info": {
   "file_extension": ".ts",
   "mimetype": "text/x.typescript",
   "name": "typescript",
   "nb_converter": "script",
   "pygments_lexer": "typescript",
   "version": "5.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
